package graphics;

import com.sun.j3d.utils.universe.SimpleUniverse;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.LineArray;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.TriangleArray;
import javax.vecmath.Point3f;
import kernel.stl.Facet;
import kernel.stl.STL;
import kernel.stl.PointXYZ;

/**
 *
 * @author George
 */
public class UniverseFactory {
    
    private static final Double FACTOR = 0.8;

    private static double calculateScale(STL stl) {
        double deltaX = stl.getSolid().getMaxX() - stl.getSolid().getMinX();
        double deltaY = stl.getSolid().getMaxY() - stl.getSolid().getMinY();
        double deltaZ = stl.getSolid().getMaxZ() - stl.getSolid().getMinZ();
        
        double max = deltaX;
        if (deltaY > max) 
            max = deltaY;
        if (deltaZ > max)
            max = deltaZ;
        
        return FACTOR * (1 / max);
    }
    
    public static SimpleUniverse newUniverse(STL stl, Canvas3D canvas) {
        Double scale = 1.0;        
        return newUniverse(stl, canvas, scale);
    }
    
    public static SimpleUniverse newUniverse(STL stl, Canvas3D canvas, 
            Double scale) {        
        if (scale == 0)
            scale = calculateScale(stl);
            
        SimpleUniverse universe = new SimpleUniverse(canvas);
        BranchGroup sceneGraph = createSceneGraph(stl, scale);        
        universe.addBranchGraph(sceneGraph);
        return universe;
    }

    private static BranchGroup createSceneGraph(STL stl, Double scale) {
        BranchGroup object3d = new BranchGroup(); 
        
        //Criação do Grafo de Cena
        object3d = new BranchGroup();

        //transformGroup permite transformações lineares em tempo de execução
        TransformGroup transformGroup = new TransformGroup();
        transformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        object3d.addChild(transformGroup);

        Shape3D shape = new Shape3D();
        TriangleArray tri = new TriangleArray(3, TriangleArray.COORDINATES);

        for (int i = 0; i < stl.getSolid().getFacetCount(); i++) {
            Facet facet = stl.getSolid().getFacet(i);
            Shape3D triangle = createTriangle(facet.getVertex1(), facet.getVertex2(),
                    facet.getVertex3());
            transformGroup.addChild(triangle);
        }

        //criação de um objeto Transform3D para alteração de escala da imagem
        //tridimensional
        Transform3D transform3D;
        transform3D = new Transform3D();
        transform3D.setScale(scale);

        transformGroup.setTransform(transform3D);
        
        object3d.compile();
        return object3d;
    }

    private static Shape3D createTriangle(PointXYZ vertex1, PointXYZ vertex2, PointXYZ vertex3) {
        Shape3D shape = new Shape3D();

        TriangleArray tri = new TriangleArray(3, TriangleArray.COORDINATES);
        tri.setCoordinate(0, new Point3f((float) vertex1.getX(), (float) vertex1.getY(), (float) vertex1.getZ()));
        tri.setCoordinate(1, new Point3f((float) vertex2.getX(), (float) vertex2.getY(), (float) vertex2.getZ()));
        tri.setCoordinate(2, new Point3f((float) vertex3.getX(), (float) vertex3.getY(), (float) vertex3.getZ()));

        //tri.setColor(0, new Color3f(255, 255, 255));

        shape.setGeometry(tri);

        LineArray la = new LineArray(6, LineArray.COORDINATES);
        la.setCoordinate(0, new Point3f((float) vertex1.getX(), (float) vertex1.getY(), (float) vertex1.getZ()));
        la.setCoordinate(1, new Point3f((float) vertex2.getX(), (float) vertex2.getY(), (float) vertex2.getZ()));
        la.setCoordinate(2, new Point3f((float) vertex1.getX(), (float) vertex1.getY(), (float) vertex1.getZ()));
        la.setCoordinate(3, new Point3f((float) vertex3.getX(), (float) vertex3.getY(), (float) vertex3.getZ()));
        la.setCoordinate(4, new Point3f((float) vertex2.getX(), (float) vertex2.getY(), (float) vertex2.getZ()));
        la.setCoordinate(5, new Point3f((float) vertex3.getX(), (float) vertex3.getY(), (float) vertex3.getZ()));

        shape.setGeometry(la);

        return shape;
    }
    
}
